package org.nimi317.web_gis.utils;

import org.nimi317.web_gis.Interface.QualityCalInterface;
import org.nimi317.web_gis.entity.BaseQuality;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * 计算AQI以及各类污染物浓度级别
 *
 * @author thunderobot
 */
public class QualityUtils<T extends BaseQuality> implements QualityCalInterface {

    private static final Map<String, int[]> map = new HashMap<>();

    private final T quality;

    static {
        map.put("so2", new int[]{0, 150, 500, 650, 800});
        map.put("so2_24", new int[]{0, 50, 150, 475, 800, 1600, 2100, 2620});
        map.put("no2", new int[]{0, 100, 200, 700, 1200, 2340, 3090, 3840});
        map.put("pm10_24", new int[]{0, 50, 150, 250, 350, 420, 500, 600});
        map.put("pm25_24", new int[]{0, 35, 75, 115, 150, 250, 350, 500});
        map.put("co", new int[]{0, 5, 10, 35, 60, 90, 120, 150});
        map.put("o3_8", new int[]{0, 100, 160, 215, 265, 800});
        map.put("o3", new int[]{0, 160, 200, 300, 400, 800, 1000, 1200});
        map.put("aqi", new int[]{0, 50, 100, 150, 200, 300, 400, 500});
    }

    public QualityUtils(T quality) {
        this.quality = quality;
    }

    public void exeAqi() {
        AtomicReference<Double> max = new AtomicReference<>((double) 0);
        exeLevel();
        this.calPm25(item -> max.set(Math.max(max.get(), item[0]))).calCo(item -> max.set(Math.max(max.get(), item[0]))).calNo2(item -> max.set(Math.max(max.get(), item[0]))).calSo2(item -> max.set(Math.max(max.get(), item[0]))).calPm10(item -> max.set(Math.max(max.get(), item[0]))).calO3(item -> max.set(Math.max(max.get(), item[0])));
        quality.setAqi(String.valueOf(Math.ceil(max.get())));
    }

    public void exeLevel() {
        this.calPm25(item -> quality.setPm25Level((int) item[1])).calCo(item -> quality.setCoLevel((int) item[1])).calNo2(item -> quality.setNo2Level((int) item[1])).calSo2(item -> quality.setSo2Level((int) item[1])).calPm10(item -> quality.setPm10Level((int) item[1])).calO3(item -> quality.setO3Level((int) item[1]));
    }

    private double[] calPm25(T quality) {
        double[] cal = cal(quality.getPm25H(), "pm25_24");
        return beyond(cal);
    }

    private double[] calPm10(T quality) {
        return beyond(cal(quality.getPm10H(), "pm10_24"));
    }

    private double[] calSo2(T quality) {
        double[] so2s = cal(quality.getSo2(), "so2");
        if (so2s[0] == 0 && so2s[1] == 0 && so2s[2] == 1) {
            return beyond(cal(quality.getSo2H(), "so2_24"));
        }
        return so2s;
    }

    private double[] calNo2(T quality) {
        return beyond(cal(quality.getNo2(), "no2"));
    }

    private double[] calO3(T quality) {
        //        if (o3s[0] == 0 && o3s[1] == 0) {
//            return cal(quality.getO3(), "o3");
//        }
        return beyond(cal(quality.getO3(), "o3"));
    }

    private double[] calCo(T quality) {
        return beyond(cal(quality.getCo(), "co"));
    }

    private double[] beyond(double[] cal) {
        if (cal[2] > 0) {
            return new double[]{500, 6};
        }
        return new double[]{cal[0], cal[1]};
    }

    private double[] cal(String s, String field) {
        double value;
        try {
            value = Double.parseDouble(s);
        } catch (NumberFormatException e) {
            return new double[]{0, 0, 0};
        }
        int[] range = map.get(field);
        double iAqi = 0;
        double level = 0;
        double beyondMax = value > range[range.length - 1] ? 1 : 0;
        for (int i = 0; i < range.length - 1; i++) {
            int bp = range[i];
            int bpH = range[i + 1];
            if (value > bp && value <= bpH) {
                int[] aqis = map.get("aqi");
                int aqiH = aqis[i + 1];
                int aqi = aqis[i];
                iAqi = (double) (aqiH - aqi) /
                       (bpH - bp) * (value - bp) + aqi;
                level = i + 1;
            }
        }
        return new double[]{iAqi, level, beyondMax};
    }

    @Override
    public QualityCalInterface calPm25(Consumer<double[]> consumer) {
        double[] doubles = calPm25(quality);
        consumer.accept(doubles);
        return this;
    }

    @Override
    public QualityCalInterface calPm10(Consumer<double[]> consumer) {
        double[] doubles = calPm10(quality);
        consumer.accept(doubles);
        return this;
    }

    @Override
    public QualityCalInterface calCo(Consumer<double[]> consumer) {
        double[] doubles = calCo(quality);
        consumer.accept(doubles);
        return this;
    }

    @Override
    public QualityCalInterface calNo2(Consumer<double[]> consumer) {
        double[] doubles = calNo2(quality);
        consumer.accept(doubles);
        return this;
    }

    @Override
    public QualityCalInterface calSo2(Consumer<double[]> consumer) {
        double[] doubles = calSo2(quality);
        consumer.accept(doubles);
        return this;
    }

    @Override
    public QualityCalInterface calO3(Consumer<double[]> consumer) {
        double[] doubles = calO3(quality);
        consumer.accept(doubles);
        return this;
    }
}
